package com.penngo.gui.dfdiff.view;

import cn.hutool.core.io.FileUtil;
import com.penngo.gui.dfdiff.utils.GB;
import com.penngo.gui.dfdiff.utils.LogUtils;
import com.penngo.gui.dfdiff.utils.ScreenUtil;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.AbstractHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;

import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.Component;
import java.awt.Color;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;

import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class RightDiffFile extends RightPanel{

//    public RightDiffFile(ProjectNode node) {
//        super(node);
//    }

    @Override
    protected void initUi() {

    }
    private Logger log = LogUtils.getLogger(RightDiffFile.class);
    private JFileChooser fileChooser = null;

    public enum DiffType {
        SAME, DIFF, NOTEXIST, UNCHECK
    }
    public enum ScrollType {
        LEFT,RIGHT
    }
    private DiffTable textAreaLeft;
    private DiffTable textAreaRight;

    private JScrollPane scrollPaneLeft;
    private JScrollPane scrollPaneRight;
    private JTextField fieldLeft;
    private JTextField fieldRight;

    private JButton buttonLeft;
    private JButton buttonRight;

    private File fileLeft;
    private File fileRight;

    public RightDiffFile(ProjectNode node){
        super(node);
        File fileLeft = node.getPathLeft();
        File fileRight = node.getPathRight();
        this.fileLeft = fileLeft;
        this.fileRight = fileRight;

        setLayout(new BorderLayout());
        initPanel();

        if(fileLeft != null && fileLeft.isFile()
                && fileRight != null && fileRight.isFile()){

            //setTitle(node.getTitle() + "-文件差异");
            diffFile(fileLeft, fileRight);
        }
    }

    private void selectPath(JTextField addreddField){
        if(fileChooser == null){
            fileChooser = new JFileChooser(".");
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        }
        int result = fileChooser.showOpenDialog(null);
        if(result == JFileChooser.APPROVE_OPTION){
            File selectFile = fileChooser.getSelectedFile();
            addreddField.setText(selectFile.getAbsolutePath());
            if(!fieldLeft.getText().equals("") && !fieldRight.getText().equals("")){
                fileLeft = new File(fieldLeft.getText());
                fileRight = new File(fieldRight.getText());
            }
            if(fileLeft != null && fileLeft.isFile() && fileRight != null && fileRight.isFile()){
                diffFile(fileLeft, fileRight);
            }
        }
    }

    private void diffFile(File leftfile, File fileRight){
        //System.out.println("crc32===left:" + Adler32Utils.getAdler32(leftfile) + ", right:" + Adler32Utils.getAdler32(fileRight));
        node.setPathLeft(leftfile);
        node.setPathRight(fileRight);
        java.util.List<FileLine> leftList = getLine(leftfile);
        java.util.List<FileLine> rightList = getLine(fileRight);

        java.util.List<FileDiffLine> diffLineList = makeDiffLineList(leftList, rightList);

        // 查找句内相似
        int prevIndexSame = -1;
        int lastIndexSame = -1;
        for(int start = 0; start < diffLineList.size(); start++){
            // 找到第一个相似和下一个相似的区间
            FileDiffLine line = diffLineList.get(start);
            if(line.type == DiffType.NOTEXIST){
                for(prevIndexSame = start - 1; prevIndexSame >= 0; prevIndexSame--){
                    if(prevIndexSame > -1){
                        FileDiffLine prevLine = diffLineList.get(prevIndexSame);
                        if(prevLine.type == DiffType.SAME){
                            break;
                        }
                    }
                }
                for(lastIndexSame = start + 1; lastIndexSame < diffLineList.size(); lastIndexSame++){
                    if(lastIndexSame < diffLineList.size()){
                        FileDiffLine lastLine = diffLineList.get(lastIndexSame);
                        if(lastLine.type == DiffType.SAME){
                            break;
                        }
                    }
                }
            }

            if(lastIndexSame != -1){
                // 处理区间内的相似
                for(int startLeft = prevIndexSame + 1; startLeft < lastIndexSame; startLeft++){
                    FileDiffLine leftLine = diffLineList.get(startLeft);
                    if(leftLine.getIndexLeft() > -1 && leftLine.getIndexRigth() == -1){
                        for(int startRight = prevIndexSame + 2; startRight < lastIndexSame; startRight++){
                            FileDiffLine rightLine = diffLineList.get(startRight);
                            if(rightLine.getIndexLeft() == -1 && rightLine.getIndexRigth() > -1){
                                double similar = similarString(leftLine.getLineLeft(), rightLine.getLineRight());
                                if(similar >= 0.5){
                                    leftLine.setIndexRigth(rightLine.getIndexRigth());
                                    leftLine.setLineRight(rightLine.getLineRight());
                                    leftLine.setType(DiffType.DIFF);
                                    rightLine.setIndexRigth(-1);
                                    rightLine.setLineRight("");
                                }
                            }
                        }
                    }
                }
                start = lastIndexSame;
                prevIndexSame = -1;
                lastIndexSame = -1;
            }
        }

        java.util.List<FileDiffLine> diffLineList2 = new ArrayList<>();
        diffLineList.forEach(line->{
            if(line.getIndexLeft() > -1 || line.getIndexRigth() > -1){
                diffLineList2.add(line);
            }
        });
        diffLineList.clear();

        textAreaLeft.setData(diffLineList2, ScrollType.LEFT);
        textAreaRight.setData(diffLineList2, ScrollType.RIGHT);
    }

    private static double similarString(String str1, String str2){
        int lenSimilar = 0;
        int startRight = 0;
        for(int left = 0; left < str1.length(); left++){
            char charLeft = str1.charAt(left);
            for(int right = startRight; right < str2.length(); right++){
                char charRight = str2.charAt(right);
                if(charLeft == charRight){
                    lenSimilar++;
                    startRight = right;
                    break;
                }
            }
        }
        double s1 = (lenSimilar * 1.0) / str1.length();
        double s2 = (lenSimilar * 1.0) / str2.length();
        return s1 < s2 ? s1 : s2;
    }

    private java.util.List<FileDiffLine> makeDiffLineList(java.util.List<FileLine> leftList, java.util.List<FileLine> rightList){
        java.util.List<FileDiffLine> diffLineList = new ArrayList<>();
        Map<Integer, java.util.List<Integer>> sameMap = new TreeMap<>(Comparator.naturalOrder());
        int startIndexRight = 0;

        for(int indexLeft = 0; indexLeft < leftList.size(); indexLeft++){
            FileLine leftLine = leftList.get(indexLeft);
            int sameCount = 0;
            int indexRight = startIndexRight;
            int indexTempRight = 0;
            // 录找唯一相同值
            for(; indexRight < rightList.size(); indexRight++){
                FileLine rightLine = rightList.get(indexRight);
                if(leftLine.getLine().equals(rightLine.getLine())){
                    sameCount++;
                    indexTempRight = indexRight;
                    java.util.List<Integer> list = sameMap.getOrDefault(indexLeft, new ArrayList<>());
                    list.add(indexRight);
                    sameMap.put(indexLeft,list);
                }
            }
            if(sameCount == 0){
                sameMap.put(indexLeft, new ArrayList<>());
            }
            else if(sameCount == 1){
                startIndexRight = indexTempRight;
            }
        }
        // 输出Map========================

        Iterator<Map.Entry<Integer, java.util.List<Integer>>> it = sameMap.entrySet().iterator();
        int rightIndex = -1;
        while(it.hasNext()){
            Map.Entry<Integer, java.util.List<Integer>> entry = it.next();
            int key = entry.getKey();
            java.util.List<Integer> list = entry.getValue();
//        sameMap.forEach((key, list)->{
            FileLine leftLine = leftList.get(key);
            if(list.isEmpty()){
                diffLineList.add(new FileDiffLine(leftLine.getIndex(), -1, leftLine.getLine(), "", DiffType.NOTEXIST));
            }
            else if(list.size() == 1){
                FileLine rightLine = rightList.get(list.get(0));
                rightIndex = rightLine.getIndex();
                diffLineList.add(new FileDiffLine(leftLine.getIndex(), rightLine.getIndex(), leftLine.getLine(), rightLine.getLine(), DiffType.SAME));
            }
            else if(list.size() > 1){
                // 如果多个相同时
                // 查找上一个相同和下一个相同
                int prevRight = -1;
                int lastRight = -1;
                for(int index = key; index > -1; index--){
                    java.util.List<Integer> prevlist = sameMap.get(index);
                    if(prevlist != null && prevlist.size() == 1){
                        prevRight = prevlist.get(0);
                        break;
                    }
                }
                for(int index = key; index < leftList.size(); index++){
                    java.util.List<Integer> lastlist = sameMap.get(index);
                    if(lastlist != null && lastlist.size() == 1){
                        lastRight = lastlist.get(0);
                        break;
                    }
                }
                if(lastRight > prevRight){
                    for(int index:list){
                        if(index > prevRight && index < lastRight && index > rightIndex){
                            FileLine rightLine = rightList.get(index);
                            rightIndex = rightLine.getIndex();
                            diffLineList.add(new FileDiffLine(leftLine.getIndex(), rightLine.getIndex(), leftLine.getLine(), rightLine.getLine(), DiffType.SAME));
                            break;
                        }
                    }
                }
            }
        };
        // 补充右边缺少的元素
        java.util.List<FileDiffLine> diffLineList2 = new ArrayList<>();
        int rightIndex1 = -1;
        int rightIndex2 = -1;
        for(int index = 0; index < diffLineList.size(); index++){
            FileDiffLine line = diffLineList.get(index);
            if(line.getIndexRigth() > -1 && rightIndex1 == -1){
                rightIndex1 = line.getIndexRigth();
            }
            if(Math.abs(line.getIndexRigth() - rightIndex1) == 1){
                rightIndex1 = line.getIndexRigth();
            }
            else if(Math.abs(line.getIndexRigth() - rightIndex1) > 1 && line.getIndexRigth() != -1){
                rightIndex2 = line.getIndexRigth();
            }

            if(rightIndex1 > -1 && rightIndex2 > -1){
                for(int rightIndex3 = rightIndex1 + 1; rightIndex3 < rightIndex2; rightIndex3++){
                    FileLine rightLine = rightList.get(rightIndex3);
                    diffLineList2.add(new FileDiffLine(-1, rightLine.getIndex(), "", rightLine.getLine(), DiffType.NOTEXIST));
                }
                rightIndex1 = rightIndex2;
                rightIndex2 = -1;
            }

            diffLineList2.add(line);

        }

        return diffLineList2;
    }

    public java.util.List<FileLine> getLine(File file){
        java.util.List<FileLine> list = new ArrayList<>();
        try{
            java.util.List<String> lineList = FileUtil.readLines(file, "UTF-8");
            for(int index = 0; index < lineList.size(); index++){
                list.add(new FileLine(index, lineList.get(index), DiffType.UNCHECK));
            }
        }
        catch(Exception e){
            log.log(Level.SEVERE, "diffFile error=====", e);
        }
        return list;
    }

    private JPanel initLeftPanel(){
        fieldLeft = new JTextField();

        if(fileLeft != null && fileLeft.exists() && fileLeft.isFile()){
            fieldLeft.setText(fileLeft.getAbsolutePath());
        }
        buttonLeft = new JButton("选择");
        buttonLeft.addActionListener(e -> selectPath(fieldLeft));
        textAreaLeft = new DiffTable();

        scrollPaneLeft = new JScrollPane(textAreaLeft);
        JPanel panel = initCodePanel(fileLeft, fieldLeft, buttonLeft, textAreaLeft, scrollPaneLeft, ScrollType.LEFT);
        return panel;
    }

    private JPanel initRightPanel(){
        fieldRight = new JTextField();
        if(fileLeft != null && fileRight.isFile() && fileRight.exists()){
            fieldRight.setText(fileRight.getAbsolutePath());
        }
        buttonRight = new JButton("选择");
        buttonRight.addActionListener(e -> selectPath(fieldRight));

        textAreaRight = new DiffTable();
        scrollPaneRight = new JScrollPane(textAreaRight);
        JPanel panel = initCodePanel(fileRight, fieldRight, buttonRight, textAreaRight, scrollPaneRight, ScrollType.RIGHT);
        return panel;
    }

    private JPanel initCodePanel(File file, JTextField field, JButton button, DiffTable textArea, JScrollPane scrollPane, ScrollType type){
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());

        scrollPane.getVerticalScrollBar().addAdjustmentListener(new SelfAdjustmentListener(type, scrollPane));

        panel.add(field, new GB(0,0,1,1).setInsets(5).setFill(GB.BOTH).setWeight(100,0));
        panel.add(button, new GB(1,0,1,1).setInsets(0));
        panel.add(scrollPane, new GB(0,1,2,1).setInsets(5).setFill(GB.BOTH).setWeight(100,100));
        return panel;
    }

    class SelfAdjustmentListener implements AdjustmentListener {
        private ScrollType type;
        private JScrollPane scrollPane;
        public SelfAdjustmentListener(ScrollType type, JScrollPane scrollPane) {
            this.type = type;
            this.scrollPane = scrollPane;
        }

        @Override
        public void adjustmentValueChanged(AdjustmentEvent e) {

            int max = scrollPane.getVerticalScrollBar().getMaximum();
            int value = e.getValue() < max ? e.getValue() : max;
            if(type == ScrollType.LEFT){
                scrollPaneRight.getVerticalScrollBar().setValue(value);
            }
            else if(type == ScrollType.RIGHT){
                scrollPaneLeft.getVerticalScrollBar().setValue(value);
            }
        }
    }

    private void initPanel(){
        JSplitPane hSplitPane = new JSplitPane();
        hSplitPane.setLeftComponent(initLeftPanel());
        hSplitPane.setRightComponent(initRightPanel());

        hSplitPane.setDividerLocation((int)(ScreenUtil.getScreenWidth() * 0.7 / 2));
        add(hSplitPane, BorderLayout.CENTER);
    }

    class FileDiffLine{
        private int indexLeft;
        private int indexRigth;
        private String lineLeft;
        private String lineRight;
        private DiffType type;

        public FileDiffLine(int indexLeft, int indexRgith, String lineLeft, String lineRight, DiffType type) {
            this.indexLeft = indexLeft;
            this.indexRigth = indexRgith;
            this.lineLeft = lineLeft;
            this.lineRight = lineRight;
            this.type = type;
        }

        public int getIndexLeft() {
            return indexLeft;
        }

        public void setIndexLeft(int indexLeft) {
            this.indexLeft = indexLeft;
        }

        public int getIndexRigth() {
            return indexRigth;
        }

        public void setIndexRigth(int indexRgith) {
            this.indexRigth = indexRgith;
        }

        public String getLineLeft() {
            return lineLeft;
        }

        public void setLineLeft(String lineLeft) {
            this.lineLeft = lineLeft;
        }

        public String getLineRight() {
            return lineRight;
        }

        public void setLineRight(String lineRight) {
            this.lineRight = lineRight;
        }

        public DiffType getType() {
            return type;
        }

        public void setType(DiffType type) {
            this.type = type;
        }

        @Override
        public String toString() {
            return "FileDiffLine{" +
                    "indexLeft=" + indexLeft +
                    ", indexRgith=" + indexRigth +
                    ", lineLeft='" + lineLeft + '\'' +
                    ", lineRight='" + lineRight + '\'' +
                    ", type=" + type +
                    '}';
        }
    }

    class DiffTable extends JXTable {
        private DiffTableModel tableModel = null;
        public DiffTable(){
            super();
            tableModel = new DiffTableModel();
            this.setTableHeader(null);
            setShowGrid(false,false);
            setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);// AUTO_RESIZE_OFF
            this.addMouseListener(new MouseListener() {
                @Override
                public void mouseClicked(MouseEvent e) {

                }

                @Override
                public void mousePressed(MouseEvent e) {

                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    int[] columns = getSelectedColumns();
                    int[] rows = getSelectedRows();
                    //log.info("mousePressed====rows:" + JSONUtil.toJsonStr(rows) + ",columns:" + JSONUtil.toJsonStr(columns.length));
                }

                @Override
                public void mouseEntered(MouseEvent e) {

                }

                @Override
                public void mouseExited(MouseEvent e) {

                }
            });
        }


        public void setData(java.util.List<FileDiffLine> diffLineList, ScrollType type){
            tableModel.setData(diffLineList, type);
            this.setHighlighters(new DiffHighlighter(diffLineList, type));
            this.setModel(tableModel);
            this.updateUI();
            getColumnModel().getColumn(0).setPreferredWidth(15);
            getColumnModel().getColumn(0).setMaxWidth(15);
            int width = this.getFontMetrics(this.getFont()).stringWidth(String.valueOf(diffLineList.size()));

            getColumnModel().getColumn(1).setPreferredWidth(width + 5); //20
            getColumnModel().getColumn(1).setMaxWidth(width + 5);
        }
    }

    class DiffTableModel extends AbstractTableModel {
        private ScrollType type;
        private java.util.List<FileDiffLine> diffLineList;
        public DiffTableModel(){

        }

        public void setData(java.util.List<FileDiffLine> diffLineList, ScrollType type){
            //log.info("DiffTableModel setData=========" + diffLineList.size());
            this.diffLineList = diffLineList;
            this.type = type;

        }

        @Override
        public int getRowCount() {
            if(diffLineList == null){
                return 0;
            }
            else{
                return diffLineList.size();
            }
        }

        @Override
        public int getColumnCount() {
            return 3;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            FileDiffLine diffLine = diffLineList.get(rowIndex);
            DiffType diffType = diffLine.getType();
            if(this.type == ScrollType.LEFT){
                switch (columnIndex){
                    case 0:
                        if(diffType == DiffType.NOTEXIST){
                            if(diffLine.getIndexLeft() == -1){
                                return "-";
                            }
                            else{
                                return "+";
                            }
                        }
                        else if(diffType == DiffType.DIFF){
                            return "*";
                        }
                        else{
                            return "";
                        }
                    case 1:
                        String str = "";
                        if(diffLine.getIndexLeft() > -1){
                            str = String.valueOf(diffLine.getIndexLeft() + 1);
                        }
                        return str;
                    case 2:
                        return diffLine.getLineLeft();
                    default:
                        return "";
                }
            }
            else if(this.type == ScrollType.RIGHT){
                switch (columnIndex){
                    case 0:
                        if(diffType == DiffType.NOTEXIST){
                            if(diffLine.getIndexRigth() == -1){
                                return "-";
                            }
                            else{
                                return "+";
                            }
                        }
                        else if(diffType == DiffType.DIFF){
                            return "*";
                        }
                        else{
                            return "";
                        }
                    case 1:
                        String str = "";
                        if(diffLine.getIndexRigth() > -1){
                            str = String.valueOf(diffLine.getIndexRigth() + 1);
                        }
                        return str;
                    case 2:
                        return diffLine.getLineRight();
                    default:
                        return "";
                }
            }
            return null;
        }

        @Override
        public String getColumnName(int column) {
            switch (column) {
                case 0:
                    return "对比结果";
                case 1:
                    return "序号";
                case 2:
                    return "文本";
                default:
                    return "";
            }
        }
    }

    class DiffHighlighter extends AbstractHighlighter {
        private java.util.List<FileDiffLine> diffLineList;
        private ScrollType type;
        public DiffHighlighter(List<FileDiffLine> diffLineList, ScrollType type){
            this.diffLineList = diffLineList;
            this.type = type;
        }

        @Override
        protected Component doHighlight(Component component, ComponentAdapter adapter) {
            FileDiffLine line = diffLineList.get(adapter.row);
            if(adapter.column == 0){
                component.setForeground(Color.RED);
                component.setBackground(Color.LIGHT_GRAY);
            }
            else if(adapter.column == 1){
                component.setForeground(Color.BLACK);
                component.setBackground(Color.LIGHT_GRAY);
            }
            else if(adapter.column == 2){
                if(line.getType() == DiffType.DIFF){
                    component.setBackground(Color.YELLOW);
                }
                else if(line.getType() == DiffType.NOTEXIST){
                    if(type == ScrollType.LEFT && line.getIndexLeft() == -1 ||
                            type == ScrollType.RIGHT && line.getIndexRigth() == -1){
                        component.setBackground(Color.LIGHT_GRAY);
                    }
                    else{
                        component.setBackground(Color.ORANGE);
                    }
                }
            }
            return component;
        }
    }

    class FileLine{
        private int index;
        private String line;
        private DiffType type;

        public FileLine(int index, String line, DiffType type) {
            this.index = index;
            this.line = line;
            this.type = type;
        }

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        public String getLine() {
            return line;
        }

        public void setLine(String line) {
            this.line = line;
        }

        @Override
        public String toString() {
            return "FileDiffLine{" +
                    "index=" + index +
                    ", line='" + line + '\'' +
                    ", type=" + type +
                    '}';
        }
    }

    public static void main(String[] args) throws Exception{

        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        File leftFile = new File("DFDiff\\logs\\customized1\\views\\bigScreen\\components\\database.vue");
        File rightFile = new File("DFDiff\\logs\\customized2\\views\\bigScreen\\components\\database.vue");
    }
}
